'''
Functions that map types to names (and vice versa) to be used by `jsonfy` and 
`unjsonfy` modules. 
'''

# For some reason, it does not find __builtins__ correctly when this file is
# not executed as __main__. When run as main, we produce the code that generates
# the builtin types that can be copy and paste back to the python source
if __name__ == '__main__':
    print 'BUILTINS = {'
    for k, v in vars(__builtins__).items():
        if isinstance(v, type):
            print "    '%s': %s," % (k, k)
    print '}'

# start copy'n'paste
BUILTINS = {
    'bytearray': bytearray,
    'IndexError': IndexError,
    'SyntaxError': SyntaxError,
    'unicode': unicode,
    'UnicodeDecodeError': UnicodeDecodeError,
    'memoryview': memoryview,
    'NameError': NameError,
    'BytesWarning': BytesWarning,
    'dict': dict,
    'SystemExit': SystemExit,
    'StandardError': StandardError,
    'RuntimeWarning': RuntimeWarning,
    'list': list,
    'Warning': Warning,
    'set': set,
    'bytes': bytes,
    'EOFError': EOFError,
    'BufferError': BufferError,
    'slice': slice,
    'FloatingPointError': FloatingPointError,
    'FutureWarning': FutureWarning,
    'ImportWarning': ImportWarning,
    'ReferenceError': ReferenceError,
    'frozenset': frozenset,
    'super': super,
    'TypeError': TypeError,
    'KeyboardInterrupt': KeyboardInterrupt,
    'UserWarning': UserWarning,
    'staticmethod': staticmethod,
    'SystemError': SystemError,
    'BaseException': BaseException,
    'RuntimeError': RuntimeError,
    'float': float,
    'MemoryError': MemoryError,
    'StopIteration': StopIteration,
    'enumerate': enumerate,
    'LookupError': LookupError,
    'basestring': basestring,
    'UnicodeError': UnicodeError,
    'long': long,
    'ImportError': ImportError,
    'xrange': xrange,
    'type': type,
    'Exception': Exception,
    'tuple': tuple,
    'UnicodeTranslateError': UnicodeTranslateError,
    'reversed': reversed,
    'UnicodeEncodeError': UnicodeEncodeError,
    'IOError': IOError,
    'SyntaxWarning': SyntaxWarning,
    'ArithmeticError': ArithmeticError,
    'str': str,
    'property': property,
    'GeneratorExit': GeneratorExit,
    'int': int,
    'KeyError': KeyError,
    'PendingDeprecationWarning': PendingDeprecationWarning,
    'file': file,
    'EnvironmentError': EnvironmentError,
    'OSError': OSError,
    'DeprecationWarning': DeprecationWarning,
    'UnicodeWarning': UnicodeWarning,
    'complex': complex,
    'bool': bool,
    'ValueError': ValueError,
    'buffer': buffer,
    'object': object,
    'TabError': TabError,
    'ZeroDivisionError': ZeroDivisionError,
    'IndentationError': IndentationError,
    'AssertionError': AssertionError,
    'classmethod': classmethod,
    'UnboundLocalError': UnboundLocalError,
    'NotImplementedError': NotImplementedError,
    'AttributeError': AttributeError,
    'OverflowError': OverflowError,
}
# end copy'n'paste

def type2name(cls):
    '''Return the full name of a type.
    
    Full name is composed by __module__.__name__, except for builtin types which
    is simply the type name.
    
    Examples
    --------
    
    >>> type2name(dict)
    u'dict'
    >>> import datetime
    >>> type2name(datetime.date)
    u'datetime.date'
    '''

    module = cls.__module__
    name = cls.__name__
    if module == '__builtin__':
        return unicode(name)
    else:
        return unicode('%s.%s' % (module, name))

def name2type(name):
    '''Return a type from its qualified name.
    
    Examples
    --------
    
    >>> name2type('xml.dom.minidom.parse') # doctest: +ELLIPSIS
    <function parse at 0x...>
    
    >>> name2type('list') # doctest: +ELLIPSIS
    <type 'list'>
    '''

    if '.' not in name:
        return BUILTINS[name]
    else:
        path = name.split('.')
        curr = __import__('.'.join(path[:-1]))
        for node in path[1:]:
            curr = getattr(curr, node)
        return curr

def load_modules(path):
    '''Tries to load all modules in the given path'''

    spath = path.split('.')
    current = ''
    for node in spath:
        current = node if not current else '%s.%s' % (current, node)
        try:
            __import__(current)
        except ImportError:
            break


if __name__ == '__main__':
    import doctest
    doctest.testmod(optionflags=doctest.REPORT_ONLY_FIRST_FAILURE, verbose=0)
